package fms.thread;

import fms.controller.*;
import fms.dao.ServiceStatusService;
import fms.model.ServiceStatus;
import fms.util.*;
import org.apache.log4j.Logger;

import java.io.*;
import java.util.*;

/**
 * Created by Administrator on 2014/9/12.
 * <p/>
 * 解析/保存航班飞行计划的线程
 */
public class FlightPlanDataThread implements Runnable {
    private static Logger logger = Logger.getLogger(FlightPlanDataThread.class);


    private String dirPath;
    private String backupPath;
    private String backupPlanSuccessData;
    private String accessMethod;
    private FtpParamVo ftpParamVo;
    private static Map<String, String> mapAirlinesCodes;

    private static final String AccessMethodFtp = "FTP";
    private static final String AccessMethodLocal = "LOCAL";

    // 飞行计划字符串编码方式
    private static final String StrEncoding = "UTF-8";

    public FlightPlanDataThread(String dirPath, String backupPath, String backupPlanSuccessData,
                                String accessMethod, FtpParamVo ftpParamVo,Map<String, String> mapAirlinesCodes) {
        this.dirPath = dirPath;
        this.backupPath = backupPath;
        this.backupPlanSuccessData = backupPlanSuccessData;
        this.accessMethod = accessMethod;
        this.ftpParamVo = ftpParamVo;
        this.mapAirlinesCodes=mapAirlinesCodes;
    }

    @Override
    public void run() {
        dealFlightPlanData(dirPath, backupPath, backupPlanSuccessData, accessMethod, ftpParamVo);
    }

    private static boolean isInAirlineMap(String fileName){

        boolean result=false;
        Iterator it=mapAirlinesCodes.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry entry= (Map.Entry) it.next();
            String value= (String) entry.getValue();
            if(fileName.contains(value)){
                result=true;
                break;
            }
        }
        return result;

    }

    /**
     * 解析飞行计划数据，从文件目录中搜索出飞行计划的文件然后进行解析，
     * 解析成功的插入到数据库中，解析不成功的放在备份目录中。然后清空原文件目录。
     */
    private static void dealFlightPlanData(String scanDirPath, String backupDirPath, String backupPlanSuccessData,
                                           String accessMethod, FtpParamVo ftpParamVo) {
        try {
            if (ObjectUtil.isBlank(accessMethod)) {
                logger.error("访问方式（FTP或LOCAL）为空！");
                return;
            }
            if (accessMethod.equals(AccessMethodFtp)) {
                dealFtpFlightPlanData(scanDirPath, backupDirPath, backupPlanSuccessData, ftpParamVo);
            }
            else if (accessMethod.equals(AccessMethodLocal)) {
                dealLocalFlightPlanData(scanDirPath, backupDirPath, backupPlanSuccessData);
            }
        } catch (Exception e) {
            logger.error("飞行计划解析程序异常：" + e.getMessage());
        }
    }

    // 用于处理本地“共享目录”类型的数据接口
    private static void dealLocalFlightPlanData(String scanDirPath, String backupDirPath, String backupPlanSuccessData) throws Exception {
        // 判断指定目录中是否存在文件
        File fSacnDir = new File(scanDirPath);
        if ((null == fSacnDir) || !fSacnDir.isDirectory()) {
            logger.error("无法打开指定数据源目录！");
            return;
        }
        IFlyPlanParseController planParseControl = null;
        String[] fileNameList = fSacnDir.list();
//        logger.debug(fileNameList + "fileNameList");
//        if (0 != fileNameList.length) {
//            logger.info(fSacnDir + "含有的文件目录总数：" + fileNameList.length);
//        }
        for (int i = 0; i < fileNameList.length; i++) {
            File childFile = new File(scanDirPath + File.separator + fileNameList[i]);
//            logger.debug(childFile.getName() + "childFile");
            // Jetplan和春秋的航班计划都放在txt文件中
            String fileTypeStr = ConfUtil.props.getProperty("FlyPlanSuffix");
            String fileTypes[] = fileTypeStr.split(",");
            if (childFile.isFile() && FileUtil.IsTargetDocByFileName(childFile, fileTypes)) {
                // xml格式的JetPlan数据处理
                if (childFile.isFile() && FileUtil.IsXmlDocByContext(scanDirPath + File.separator + fileNameList[i])) {
                    if(fileNameList[i].startsWith("C")){
                        logger.info("检测到CES XML格式航班计划，开始解析" + fileNameList[i] + "！");
                        planParseControl = new SABREXmlFlightPlanParseController();
                    }else{
                        logger.info("检测到Jetplan格式航班计划，开始解析" + fileNameList[i] + "！");
                        planParseControl = new JetplanParseController();
                    }
                } else {
                    logger.info("检测到文本格式航班飞行计划数据，开始解析" + fileNameList[i] + "！");
                    if(isInAirlineMap(fileNameList[i])){
//                    if(fileNameList[i].contains("MU")){
                        planParseControl = new SABREFlyPlanParseController(fileNameList[i]);
                    }
                    else{
                        planParseControl = new CQFlyPlanParseController();
                    }
                }
            } else {
//                logger.error("数据源目录中存在与飞行计划无关文件：" + childFile.getName());
                continue;
            }

            FileReader fr = null;
            BufferedReader bufReader = null;
            StringBuilder strBuilderContext = new StringBuilder(409600);
            String strLine = null;
            try {
                fr = new FileReader(childFile);
                // 保存飞行计划原文
                bufReader = new BufferedReader(fr);
                while ((strLine = bufReader.readLine()) != null) {
                    strBuilderContext.append(strLine + System.getProperty("line.separator"));
                }
            } catch (Exception e) {
                throw new Exception("本地共享目录方式打开飞行计划失败！");
            }

            boolean parseStatus = planParseControl.parseDataSource(strBuilderContext.toString());

            try {
                if (bufReader != null)
                    bufReader.close();
                if (fr != null)
                    fr.close();
            } catch (IOException ex) {
                throw new Exception("本地共享目录方式关闭飞行计划文件流失败！");
            }

            if (parseStatus) {
                logger.info("航班飞行计划数据" + childFile.getName() + "解析完毕，开始入库！");
                planParseControl.saveToDB();
                //System.gc();
                //配置是否备份解析成功的文件
                String isBackupSuccess = ConfUtil.props.getProperty("isBackupSuccess");
                if ("true".equalsIgnoreCase(isBackupSuccess)) {
                    if (!FileUtil.MoveFile(childFile, backupPlanSuccessData)) {
                        // 如果文件移动失败（eg：文件名重复，则直接删除）
                        if (!childFile.delete()) {
                            logger.error(childFile.getAbsolutePath() + "解析成功的飞行计划文件删除失败，本服务停止！");
                            return;
                        } else {
                            logger.error("本次航班飞行计划解析成功，文件" + fileNameList[i] + "无法移动，已删除！");
                        }
                    } else {
                        logger.info("本次航班飞行计划解析成功，文件" + fileNameList[i] + "已被剪切至" + backupPlanSuccessData + "！");
                    }
                } else {
                    if (!childFile.delete()) {
                        logger.error(childFile.getAbsolutePath() + "文件删除失败，本服务停止！");
                        return;
                    }
                }
            } else {
                // 如果文件移动失败（eg：文件名重复，则直接删除）
                if (!FileUtil.MoveFile(childFile, backupDirPath)) {
                    if (!childFile.delete()) {
                        logger.error(childFile.getAbsolutePath() + "无法解析的飞行计划文件删除失败，本服务停止！");
                        return;
                    } else {
                        logger.error("本次航班飞行计划无法解析，文件" + fileNameList[i] + "无法移动，已删除！");
                    }
                } else {
                    logger.error("本次航班飞行计划无法解析，文件" + fileNameList[i] + "已被剪切至" + backupDirPath + "！");
                }
            }
        }
    }



    private static void dealFtpFlightPlanData(String scanDirPath, String backupDirPath,
                                              String backupPlanSuccessData, FtpParamVo ftpParamVo) throws Exception {
// 测试使用
//        boolean bRename = ftpClient.renameFile("/2.txt","/BakupPlanSuccessData/2.txt");
//        System.out.println("dealFtpFlightPlanData-bRename:" + bRename);
//boolean bDel = ftpClient.deleteFile("/2.txt");
//System.out.println("dealFtpFlightPlanData-bDel:" + bDel);

        IFlyPlanParseController planParseControl = null;
        // 用于读取ftp文件
        FtpUtils ftpReader = null;
        // 用于移动和删除ftp文件
        FtpUtils ftpMoveDel = null;

        // FTP接口状态监控
        ServiceStatusService serviceStatusService = ConfUtil.getSingletonApplicationContextDao().getBean("serviceStatusService", ServiceStatusService.class);
        ServiceStatus curFTPstatus = null;
        try {
            ftpReader = new FtpUtils(ftpParamVo.getIp(), ftpParamVo.getUserName(),
                    ftpParamVo.getPassWord(), ftpParamVo.getPort(), scanDirPath);
            // 参数初始化
            ftpMoveDel = new FtpUtils(ftpParamVo.getIp(), ftpParamVo.getUserName(),
                    ftpParamVo.getPassWord(), ftpParamVo.getPort(), scanDirPath);
            ftpReader.connectServer();

            curFTPstatus = serviceStatusService.findByServicecode(SysConstants.FTP_CODE);
            // 如果，数据库中没有FTP状态信息
            if (null == curFTPstatus) {
                curFTPstatus = new ServiceStatus();
            }
            // 为了减轻数据库的压力，只有在状态发生变化的时候，对时间进行更新
            // 如果，数据库中没有FTP状态信息 or 数据库中已有状态为异常的信息；就更新为正常
            if ( ObjectUtil.isBlank(curFTPstatus.getStatus())
                    || curFTPstatus.getStatus().equals(SysConstants.SERVICE_EXCEPTION_CODE) ) {
                curFTPstatus.setServiceName(SysConstants.FTP_CODE);
                curFTPstatus.setServiceCode(SysConstants.FTP_CODE);
                // 获取当前系统时间（北京时间）
                curFTPstatus.setUpdateTime(new Date());
                curFTPstatus.setStatus(SysConstants.SERVICE_NORMAL_CODE);
                serviceStatusService.saveOrUpdate(curFTPstatus);
                logger.info("FTP运行状态更新为正常！.");
            }
        } catch (Exception e) {
            curFTPstatus = serviceStatusService.findByServicecode(SysConstants.FTP_CODE);
            // 如果，数据库中没有FTP状态信息
            if (null == curFTPstatus) {
                curFTPstatus = new ServiceStatus();
            }
            // 为了减轻数据库的压力，只有在状态发生变化的时候，对时间进行更新
            // 如果，数据库中没有FTP状态信息 or 数据库中已有状态为正常的信息，更新为异常
            if ( ObjectUtil.isBlank(curFTPstatus.getStatus())
                    || curFTPstatus.getStatus().equals(SysConstants.SERVICE_NORMAL_CODE) ) {
                curFTPstatus.setServiceName(SysConstants.FTP_CODE);
                curFTPstatus.setServiceCode(SysConstants.FTP_CODE);
                // 获取当前系统时间（北京时间）
                curFTPstatus.setUpdateTime(new Date());
                curFTPstatus.setStatus(SysConstants.SERVICE_EXCEPTION_CODE);
                serviceStatusService.saveOrUpdate(curFTPstatus);
                logger.info("FTP运行状态更新为异常！");
            }
            throw new Exception("无法连接FTP数据源");
        }

        List fileNameList = ftpReader.getFileList(scanDirPath);
        ftpReader.closeServer();

//        logger.debug(fileNameList + "fileNameList");
//        if (0 != fileNameList.length) {
//            logger.info(fSacnDir + "含有的文件目录总数：" + fileNameList.length);
//        }
        for (int i = 0; i < fileNameList.size(); i++) {
//        for (int i = 0; i < 1; i++) {
//            System.out.println(fileNameList.get(i));
            // Jetplan和春秋的航班计划都放在txt文件中
            String fileTypeStr = ConfUtil.props.getProperty("FlyPlanSuffix");
            String fileTypes[] = fileTypeStr.split(",");
            InputStream isFtpFile = null;
            InputStreamReader iSReaderFtpFile = null;
            BufferedReader bufReader = null;
            String strContext = null;
            String strLine = null;

            if ( FileUtil.IsTargetDocByFileName((String)fileNameList.get(i),fileTypes)) {
                try {
                    ftpReader.connectServer();
                    strContext = ftpReader.getFtpFileInputStream(scanDirPath + "/" + fileNameList.get(i));
                    ftpReader.closeServer();

                } catch (IOException e) {
                    throw new Exception("FTP方式读取、关闭飞行计划失败！");
                }

                // xml格式的飞行计划数据处理
                if (FileUtil.IsXmlDocByContextStr(strContext)) {
                    if(fileNameList.get(i).toString().startsWith("C")){
                        logger.info("检测到SABRE XML格式航班计划，开始解析" + fileNameList.get(i).toString() + "！");
                        planParseControl = new SABREXmlFlightPlanParseController();
                    }else{
                        logger.info("检测到Jetplan格式航班计划，开始解析" + fileNameList.get(i).toString() + "！");
                        planParseControl = new JetplanParseController();
                    }
                }
                else {
                    logger.info("检测到文本格式航班飞行计划数据，开始解析" + fileNameList.get(i) + "！");
                    if(isInAirlineMap((String)fileNameList.get(i))){
//                    if(fileNameList[i].contains("MU")){
                        planParseControl = new SABREFlyPlanParseController((String)fileNameList.get(i));
                    }
                    else{
                        planParseControl = new CQFlyPlanParseController();
                    }
                }
            } else {
//                logger.error("数据源目录中存在与飞行计划无关文件：" + childFile.getName());
                continue;
            }

            boolean parseStatus = planParseControl.parseDataSource(strContext);

            ftpMoveDel.connectServer();
            if (parseStatus) {
                logger.info("航班飞行计划数据" + fileNameList.get(i) + "解析完毕，开始入库！");
                planParseControl.saveToDB();
//                //System.gc();
                //配置是否备份解析成功的文件
                String isBackupSuccess = ConfUtil.props.getProperty("isBackupSuccess");
                if ("true".equalsIgnoreCase(isBackupSuccess)) {

                    if (!ftpMoveDel.renameFile(scanDirPath + fileNameList.get(i),
                            backupPlanSuccessData + "/" + fileNameList.get(i))) {
                        // 如果文件移动失败（eg：文件名重复，则直接删除）
                        if (!ftpMoveDel.deleteFile( (String)fileNameList.get(i) )) {
                            logger.error(fileNameList.get(i) + "解析成功的飞行计划文件删除失败，本服务停止！");
                            ftpMoveDel.closeServer();
                            return;
                        } else {
                            logger.error("本次航班飞行计划解析成功，文件" + fileNameList.get(i) + "无法移动，已删除！");
                        }
                    } else {
                        logger.info("本次航班飞行计划解析成功，文件" + fileNameList.get(i) + "已被剪切至" + backupPlanSuccessData + "！");
                    }
                } else {
                    if (!ftpMoveDel.deleteFile( (String)fileNameList.get(i) )) {
                        logger.error(fileNameList.get(i) + "文件删除失败，本服务停止！");
                        ftpMoveDel.closeServer();
                        return;
                    } else {
                        logger.info("航班飞行计划数据" + fileNameList.get(i) + "入库结束，文件已删除！");
                    }
                } // if ("true".equalsIgnoreCase(isBackupSuccess)) {
            } else {
                // 如果文件移动失败（eg：文件名重复，则直接删除）
                if (!ftpMoveDel.renameFile(scanDirPath + fileNameList.get(i),
                        backupDirPath + "/" + fileNameList.get(i))) {
                    if (!ftpMoveDel.deleteFile( (String)fileNameList.get(i) )) {
                        logger.error(fileNameList.get(i) + "无法解析的飞行计划文件删除失败，本服务停止！");
                        ftpMoveDel.closeServer();
                        return;
                    } else {
                        logger.error("本次航班飞行计划无法解析，文件" + fileNameList.get(i) + "无法移动，已删除！");
                    }
                } else {
                    logger.error("本次航班飞行计划无法解析，文件" + fileNameList.get(i) + "已被剪切至" + backupDirPath + "！");
                }
            } // if (parseStatus) {
            ftpMoveDel.closeServer();
        } // for (int i = 0; i < fileNameList.length; i++) {

    } // dealFtpFlightPlanData()

}
